`timescale 1ns / 1ps

module dwpw_layer_l6_pe192
#(
    parameter N_IN      = 2,
    parameter N_OT      = 1,
    parameter N_ICH     = 1280,
    parameter N_OCH     = 96,
    parameter BIT_IM    = 8,
    parameter BIT_WT    = 5,
    parameter BIT_CV    = 16,
    parameter BIT_NB    = 16,
    parameter BIT_NM    = 16,
    parameter ROW       = 20,
    parameter COL       = 40,
    // dwc
    parameter DW_INW    = 16,
    parameter DW_ACT    = 2,
    parameter DW_PAD    = 0,
    parameter DWCV_PE   = "DSP",
    parameter DWET_FILE = "L10_DW.mem",
    parameter DWET_TYPE = "block",
    parameter DWET_LATENCY = 2,
    // im2col buffer RAM
    parameter DBUF_TYPE = "block",
    parameter DBUF_LATENCY = 2,
    // dwc norm
    parameter DNORM_PE   = "DSP",
    parameter DNORM_FILE = "L10_BM.mem",
    parameter DNORM_TYPE = "block",
    parameter DNORM_LATENCY = 2,
    // pwc
    parameter PW_INH    = 4,
    parameter PW_INW    = 5,
    parameter PW_OUT    = 6,
    parameter PW_ACT    = 1,
    parameter PWCV_PE    = "DSP",
    parameter PWET_FILE  = "L11_PW.mem",
    parameter PWET_TYPE  = "block",
    parameter PWET_LATENCY = 2,
    parameter PBUF_TYPE  = "block",
    parameter PBUF_LATENCY = 2,
    // pwc norm
    parameter PNORM_PE    = "DSP",
    parameter PNORM_FILE  = "L11_BM.mem",
    parameter PNORM_TYPE  = "block",
    parameter PNORM_LATENCY = 2,
    // pwc reorder fifo
    parameter PFIFO_TYPE  = "block",
    parameter PFIFO_LATENCY = 2
)
(
    input   clk,
    input   rst,
    
    input   i_vld,
    output  i_rdy,
    input   [N_IN * BIT_IM -1 : 0]  i_data,
    
    output  o_vld,
    input   o_rdy,
    output  [N_OT * BIT_IM -1 : 0]  o_data
);

wire ex0_rdy;
wire ex0_vld;
wire [DW_INW * BIT_IM - 1 : 0] ex0_data;

generate
    if (DW_INW > N_IN) begin
        expand #(
            .N     ( DW_INW / N_IN ),
            .WIDTH ( N_IN * BIT_IM )
        ) inst_expand_0 (
            .clk                     ( clk      ),
            .rst                     ( rst      ),

            .i_rdy                   (   i_rdy  ),
            .i_vld                   (   i_vld  ),
            .i_data                  (   i_data ),

            .o_rdy                   ( ex0_rdy  ),
            .o_vld                   ( ex0_vld  ),
            .o_data                  ( ex0_data )
        );
    end
    else begin
        assign i_rdy = ex0_rdy;
        assign ex0_vld = i_vld;
        assign ex0_data = i_data;
    end
endgenerate

wire dwc_rdy;
wire dwc_vld;
wire [DW_ACT * BIT_IM - 1 : 0] dwc_data;

dwc_layer #(
    .N_INW        ( DW_INW      ),
    .N_ACT        ( DW_ACT      ),
    .N_CH         ( N_ICH       ),
    .COL          ( COL         ),
    .ROW          ( ROW         ),
    .BIT_IN       ( BIT_IM      ),
    .BIT_WT       ( BIT_WT      ),
    .BIT_CV       ( BIT_CV      ),
    .BIT_NB       ( BIT_NB      ),
    .BIT_NM       ( BIT_NM      ),
    .BIT_OT       ( BIT_IM      ),
    .PADVAL       ( DW_PAD      ),
    .DWCV_PE      ( DWCV_PE         ),
    .DWET_FILE    ( DWET_FILE       ),
    .DWET_TYPE    ( DWET_TYPE       ),
    .DWET_LATENCY ( DWET_LATENCY    ),
    .DBUF_TYPE    ( DBUF_TYPE       ),
    .DBUF_LATENCY ( DBUF_LATENCY    ),
    .NORM_PE      ( DNORM_PE        ),
    .NORM_FILE    ( DNORM_FILE      ),
    .NORM_TYPE    ( DNORM_TYPE      ),
    .NORM_LATENCY ( DNORM_LATENCY   )
)
inst_dwc_layer (
    .clk                     ( clk      ),
    .rst                     ( rst      ),

    .i_rdy                   ( ex0_rdy  ),
    .i_vld                   ( ex0_vld  ),
    .i_data                  ( ex0_data ),

    .o_rdy                   ( dwc_rdy  ),
    .o_vld                   ( dwc_vld  ),
    .o_data                  ( dwc_data )
);

wire ex1_rdy;
wire ex1_vld;
wire [10 * BIT_IM - 1 : 0] ex1_data;

expand #(
    .N     ( 10 / DW_ACT     ),
    .WIDTH ( DW_ACT * BIT_IM )
) inst_expand_1 (
    .clk                     ( clk      ),
    .rst                     ( rst      ),

    .i_rdy                   ( dwc_rdy  ),
    .i_vld                   ( dwc_vld  ),
    .i_data                  ( dwc_data ),

    .o_rdy                   ( ex1_rdy  ),
    .o_vld                   ( ex1_vld  ),
    .o_data                  ( ex1_data )
);

wire rd1_rdy;
wire rd1_vld;
wire [PW_INW * BIT_IM -1 : 0] rd1_data;

reduce2 #(
    .N      ( 10 / PW_INW           ),
    .WIDTH  ( PW_INW * BIT_IM       )
) inst_reduce (
    .clk                     ( clk      ),
    .rst                     ( rst      ),

    .i_rdy                   ( ex1_rdy  ),
    .i_vld                   ( ex1_vld  ),
    .i_data                  ( ex1_data ),

    .o_rdy                   ( rd1_rdy  ),
    .o_vld                   ( rd1_vld  ),
    .o_data                  ( rd1_data )
);

wire pwc_rdy;
wire pwc_vld;
wire [PW_ACT * BIT_IM - 1 : 0] pwc_data;

pwc2_layer #(
    .N_INH        ( PW_INH        ),
    .N_INW        ( PW_INW        ),
    .N_OUT        ( PW_OUT        ),
    .N_ACT        ( PW_ACT        ),
    .N_ICH        ( N_ICH         ),
    .N_OCH        ( N_OCH         ),
    .BIT_IN       ( BIT_IM        ),
    .BIT_WT       ( BIT_WT        ),
    .BIT_CV       ( BIT_CV        ),
    .BIT_NB       ( BIT_NB        ),
    .BIT_NM       ( BIT_NM        ),
    .BIT_OT       ( BIT_IM        ),
    .PWCV_PE      ( PWCV_PE       ),
    .PWET_FILE    ( PWET_FILE     ),
    .PWET_TYPE    ( PWET_TYPE     ),
    .PWET_LATENCY ( PWET_LATENCY  ),
    .PBUF_TYPE    ( PBUF_TYPE     ),
    .PBUF_LATENCY ( PBUF_LATENCY  ),
    .NORM_PE      ( PNORM_PE      ),
    .NORM_FILE    ( PNORM_FILE    ),
    .NORM_TYPE    ( PNORM_TYPE    ),
    .NORM_LATENCY ( PNORM_LATENCY ),
    .FIFO_TYPE    ( PFIFO_TYPE    ),
    .FIFO_LATENCY ( PFIFO_LATENCY )
)
inst_pwc2_layer (
    .clk                     ( clk      ),
    .rst                     ( rst      ),

    .i_rdy                   ( rd1_rdy  ),
    .i_vld                   ( rd1_vld  ),
    .i_data                  ( rd1_data ),

    .o_rdy                   ( pwc_rdy  ),
    .o_vld                   ( pwc_vld  ),
    .o_data                  ( pwc_data )
);

generate
    if (N_OT > PW_ACT) begin
        expand #(
            .N     ( N_OT / PW_ACT   ),
            .WIDTH ( PW_ACT * BIT_IM )
        ) inst_expand_2 (
            .clk                     ( clk      ),
            .rst                     ( rst      ),

            .i_rdy                   ( pwc_rdy  ),
            .i_vld                   ( pwc_vld  ),
            .i_data                  ( pwc_data ),

            .o_rdy                   (   o_rdy  ),
            .o_vld                   (   o_vld  ),
            .o_data                  (   o_data )
        );
    end
    else begin
        assign pwc_rdy = o_rdy;
        assign o_vld = pwc_vld;
        assign o_data = pwc_data;
    end
endgenerate

endmodule
